/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usb_device.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "key_scan.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* n ∈ [1,12] */
#define GETI(n) (((I##n##_GPIO_Port->IDR & I##n##_Pin) == 0) ? (1 << n) : 0)
/* n ∈ [0,7] */
#define GETK(n) (((KEY##n##_GPIO_Port->IDR & KEY##n##_Pin) == 0) ? (1 << n) : 0)
/* n ∈ [1,4] */
#define CTLOE(n, s) (OE##n##_GPIO_Port->BSRR |= ((s) ? OE##n##_Pin : ((uint32_t)OE##n##_Pin << 16)))

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim4;

UART_HandleTypeDef huart1;
UART_HandleTypeDef huart3;

/* USER CODE BEGIN PV */
/*	 BC759x连接于USART3  				*/
/*	 PB10  (MCU TX) --> BC759x RX	*/
/*	 PB11 (MCU RX) --> BC759x TX	*/

/**** BC759X 指令	****/
// 寄存器操作指令
#define DIRECT_WRITE 0x00        // 直接写入显示寄存器，低5位为地址
#define SEG_OFF      0xC0        // 段熄灭
#define SEG_ON       0xC1        // 段点亮
#define WRITE_ALL    0xF1        // 全局写入，数据写入所有显示寄存器
// 数码管相关指令
#define DECODE_WT  0x80        // 译码显示，低5位为地址
#define DECODE_EXT 0xB0        // 扩展显示位译码显示，低2位为地址
// 点阵显示相关指令
#define COL_WRITE  0x00        // 列写入，低5位为地址
#define SHIFT_H_WT 0x40        // 插入并向右(高地址)平移，低5位为地址
#define SHIFT_L_WT 0x60        // 插入并向左(低地址)平移，低5位为地址
#define ROTATE_R   0x5F        // 向右(高地址)循环滚屏
#define ROTATE_L   0x60        // 向左(低地址)循环滚屏
#define QTR_WT_BOT 0xA0        // 写入底部 1/4 行，低2为为地址
#define QTR_WT_TOP 0xA8        // 写入顶部 1/4 行，低2位为地址
#define COORD_OFF  0xC0        // 坐标点熄灭
#define COORD_ON   0xC1        // 坐标点点亮
// 控制指令
#define BLINK_WT_SET  0x30          // 段闪烁控制置1，低4位为地址
#define BLINK_WT_CLR  0x20          // 段闪烁控制置0，低4位为地址
#define BLINK_DIG_CTL 0xD0          // 位闪烁控制，
#define BLINK_SPEED   0xF2          // 闪烁速度控制
#define DIM_CTL       0xF3          // 亮度控制
#define GLOBAL_CTL    0xF0          // 整体控制(闪烁开/关，显示开/关)
#define RESET         0xFF5A        // 复位
#define UART_SEND_0   0xFFFF        // UART	发送 0x00

// 程序有关的常量定义
#define KEY_PLUS    5         // 定义5号键为“+”键
#define KEY_MINUS   6         // 定义6号键为“-”键
#define KEY_SHIFT   2         // 定义2号键为"SHIFT"键(组合键)
#define SHIFT_PLUS  96        // BC759X最大可能产生的键值为95, 将键值96-126用作表示组合键和长按键
#define SHIFT_MINUS 97        // 两个组合键分别分配键号96和97
#define LP_PLUS     98        // “+“键长按定义键值为98
#define LP_MINUS    99        // "-"键长按定义键值为99

void delay_1ms(unsigned int Ms);
void bc759x_cmd_send(unsigned char Cmd, unsigned char Data);        // 发送一个完整指令
void display_dec(unsigned char Pos, unsigned char Number);          // 在Pos显示位以10进制显示数字
void display_bar16(unsigned char Pos, unsigned int BarVal);         // 在Pos位显示16段光柱
void send_byte(unsigned char ch);                                   // 输出一个字节到UART发送缓冲区

#define UART_BUF_SIZE 8                                // UART	FIFO 的大小
unsigned char          UartFifo[UART_BUF_SIZE];        // 定义一个串口发送缓冲区
unsigned char          NextWritePos  = 0;
volatile unsigned char NextReadPos   = 0;
volatile unsigned char FifoFreeSpace = UART_BUF_SIZE;

/********** 定义组合键 ******************************************/
const unsigned char  shift_plus[]  = { 2, SHIFT_PLUS, KEY_SHIFT, KEY_PLUS };          // 组合键1,由SHIFT键和PLUS键组成，组合键键值定义为SHIFT_PLUS
const unsigned char  shift_minus[] = { 2, SHIFT_MINUS, KEY_SHIFT, KEY_MINUS };        // 组合键2,由SHIFT键和MINUS键组成，组合键键值定义为SHIFT_MINUS
const unsigned char* CBKeyList[]   = { shift_plus, shift_minus };
unsigned char        CBMap[2];        // 供键盘驱动库使用的组合键映射数组

/********** 定义长按键 *****************************************/
const unsigned char  lp_plus[]   = { KEY_PLUS, LP_PLUS };          // 定义"+"的长按键
const unsigned char  lp_minus[]  = { KEY_MINUS, LP_MINUS };        // 定义”-“的长按键
const unsigned char  nokey[]     = { 0xff, 0xff };                 // 定义”无按键“
const unsigned char* LPKeyList[] = { lp_plus, lp_minus, nokey };

volatile unsigned int DownCounter;           //	延时计时器
unsigned char         Value = 100;           //	显示的数值
unsigned int          Bar1  = 0x0001;        //	显示的光柱值
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
uint8_t u3rxbuf[1] = {
    0,
};
uint8_t u3txbuf[1] = {
    0,
};
uint16_t g_digital_input_i = 0;
uint16_t g_digital_input_key[4] = {0, 0, 0, 0};
uint16_t g_pulse = 0;
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
// 输出一个字节到UART发送缓冲区
void send_byte(unsigned char ch)
{
    while (FifoFreeSpace == 0)
        ;                                                                     // 检查FIFO中是否还有空间
//    __HAL_UART_DISABLE_IT(&huart3,UART_IT_TXE);                               // 禁止串口发送中断防止冲突
    --FifoFreeSpace;                                                          // FIFO空间数-1
    UartFifo[NextWritePos] = ch;                                              // 写入新数据
    NextWritePos           = (NextWritePos + 1) & (UART_BUF_SIZE - 1);        // 调整写指针到下一单元
    if (FifoFreeSpace == (UART_BUF_SIZE - 1))                                 // 如果之前FIFO已经清空
    {
        //USART1->DR  = ch;                                                   // 启动串口发送
        u3txbuf[0] = ch;
        HAL_UART_Transmit_IT(&huart3,u3txbuf,1);
        NextReadPos = (NextReadPos + 1) & (UART_BUF_SIZE - 1);                // 调整读指针到下一单元
    }
//    __HAL_UART_ENABLE_IT(&huart3,UART_IT_TXE);                                // 使能TXEIE中断
}

// 通用延时程序
void delay_1ms(unsigned int Ms)
{
    DownCounter = Ms + 1;
    while (DownCounter != 0)
        ;
}

// 发送一个完整指令
void bc759x_cmd_send(unsigned char Cmd, unsigned char Data)
{
    send_byte(Cmd);
    send_byte(Data);
}

// 在Pos显示位以10进制显示数字
void display_dec(unsigned char Pos, unsigned char Number)
{
    unsigned char x;
    unsigned char NotFirstDigit = 0;
    Pos &= 0x1f;             //	确保pos值在有效范围内
    x = Number / 100;        //	得到百位
    if (0 != x)
    {
        bc759x_cmd_send(DECODE_WT | Pos, x);        // 显示百位
        NotFirstDigit = 1;
    }
    else
    {
        bc759x_cmd_send(DIRECT_WRITE | Pos, 0);        // 首位如果为0，不显示
    }
    Number = Number % 100;
    x      = Number / 10;                 // 得到十位
    if (NotFirstDigit || (0 != x))        // 如果十位不为0或不是首位
    {
        bc759x_cmd_send(DECODE_WT | (Pos + 1), x);
    }
    else
    {
        bc759x_cmd_send(DIRECT_WRITE | (Pos + 1), 0);        //	如果为0且是首位，不显示
    }
    Number = Number % 10;        // 得到个位
    bc759x_cmd_send(DECODE_WT | (Pos + 2), Number);
}

// 在Pos位显示16段光柱
void display_bar16(unsigned char Pos, unsigned int Bar)
{
    Pos &= 0x1f;                                           // 确保Pos值在有效范围内
    bc759x_cmd_send(DIRECT_WRITE | Pos, Bar >> 8);         // 写光柱高8段
    bc759x_cmd_send(DIRECT_WRITE | (Pos + 1), Bar);        // 写光柱低8段
}
/**
 * @brief 获取数字输入
 *
 */
void read_di(void)
{
    uint32_t di;
    di = GETI(1) | GETI(2) | GETI(3) | GETI(4) |
         GETI(5) | GETI(6) | GETI(7) | GETI(8) |
         GETI(9) | GETI(10) | GETI(11) | GETI(12);
    g_digital_input_i = di >> 1;
}
/**
 * @brief 获取旋钮值
 *
 * @param n  =0：清零，复位OE
 * ∈ [1,4] 读取单个旋钮
 * ∈ [5,255] 读取所有旋钮值
 */
void read_xn(uint8_t n)
{
    uint32_t di;
    switch (n)
    {
    case 0:
        CTLOE(1, 1);
        CTLOE(2, 1);
        CTLOE(3, 1);
        CTLOE(4, 1);
        memset(g_digital_input_key, 0, sizeof(g_digital_input_key));
        break;
    case 1:
        CTLOE(1, 0);
        di = GETK(0) | GETK(1) | GETK(2) |
             GETK(3) | GETK(4) | GETK(5);
        g_digital_input_key[0] = di;
        CTLOE(1, 1);
        break;
    case 2:
        CTLOE(2, 0);
        di = GETK(0) | GETK(1) | GETK(2) |
             GETK(3) | GETK(4) | GETK(5);
        g_digital_input_key[1] = di;
        CTLOE(2, 1);
        break;
    case 3:
        CTLOE(3, 0);
        di = GETK(0) | GETK(1) | GETK(2) |
             GETK(3) | GETK(4) | GETK(5);
        g_digital_input_key[2] = di;
        CTLOE(3, 1);
        break;
    case 4:
        CTLOE(4, 0);
        di = GETK(0) | GETK(1) | GETK(2) |
             GETK(3) | GETK(4) | GETK(5);
        g_digital_input_key[3] = di;
        CTLOE(4, 1);
        break;
    default:
        CTLOE(1, 0);
        di = GETK(0) | GETK(1) | GETK(2) |
             GETK(3) | GETK(4) | GETK(5);
        g_digital_input_key[0] = di;
        CTLOE(1, 1);
        CTLOE(2, 0);
        di = GETK(0) | GETK(1) | GETK(2) |
             GETK(3) | GETK(4) | GETK(5);
        g_digital_input_key[1] = di;
        CTLOE(2, 1);
        CTLOE(3, 0);
        di = GETK(0) | GETK(1) | GETK(2) |
             GETK(3) | GETK(4) | GETK(5);
        g_digital_input_key[2] = di;
        CTLOE(3, 1);
        CTLOE(4, 0);
        di = GETK(0) | GETK(1) | GETK(2) |
             GETK(3) | GETK(4) | GETK(5);
        g_digital_input_key[3] = di;
        CTLOE(4, 1);
        break;
    }
}
/**
 * @brief 读取脉冲计数
 * 
 */
void read_pulse(void)
{
    g_pulse = TIM4->CNT;
}
/**
 * @brief 系统滴答中断钩子函数 周期1ms
 * 
 */
void SysTick_Handler_Hook(void)        // 系统滴答定时器中断服务程序
{
    if (DownCounter != 0)        // 如果计数器不为零，则减一	(通用延时用)
    {
        --DownCounter;
    }
    long_press_tick();        // 呼叫键盘驱动库长按键计数函数
}
/**
 * @brief USART接收完成回调
 * 
 * @param huart 
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART3)
    {
        update_key_status(u3rxbuf[0]);
        HAL_UART_Receive_IT(huart,&u3rxbuf[0],1);
    }
}
/**
 * @brief 串口发送完成回调
 * 
 * @param huart 
 */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART3)
    {
        ++FifoFreeSpace;                          // 可用空间+1
        if (FifoFreeSpace < UART_BUF_SIZE)        // 如果FIFO尚未全部发送
        {
            //USART1->DR  = UartFifo[NextReadPos];        // 发送下一字符
            u3txbuf[0] = UartFifo[NextReadPos];
            HAL_UART_Transmit_IT(&huart3,u3txbuf,1);
            NextReadPos = (NextReadPos + 1) & (UART_BUF_SIZE - 1);
        }
        else
        {
            //USART1->CR1 &= ~USART_CR1_TXEIE;        // 缓冲区发送完成，禁止TXEIE中断
        }
    }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
  
    unsigned char NoKeyTimer = 0;
    unsigned char NewKey;
    uint32_t main_loop_cnt = 0;
    /* USER CODE END 1 */

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_USB_DEVICE_Init();
    MX_USART3_UART_Init();
    MX_TIM4_Init();
    /* USER CODE BEGIN 2 */
    HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL);
    HAL_UART_Receive_IT(&huart3, &u3rxbuf[0], 1);
    set_detect_mode(1);                           // 检测按键释放
    set_longpress_count(3000);                    // 设置长按键时间为3秒
    def_combined_key(CBKeyList, CBMap, 2);        // 设置组合键
    def_longpress_key(LPKeyList, 3);              // 设置长按键和无按键检测
    delay_1ms(300);                               // 延时300ms，确保BC759X进入工作状态
    bc759x_cmd_send(WRITE_ALL, 0xff);             // 亮点全部显示段
    delay_1ms(1500);                              // 显示 1500ms
    HAL_GPIO_WritePin(RUN_GPIO_Port, RUN_Pin, GPIO_PIN_RESET); // 显示 1500ms
    bc759x_cmd_send(WRITE_ALL, 0x00);                          // 清除显示
    bc759x_cmd_send(BLINK_SPEED, 20);                          // 设置闪烁速度
    bc759x_cmd_send(BLINK_WT_SET | 5, 0xff);                   // 设置第8，9，10显示位为闪烁显示
    bc759x_cmd_send(BLINK_WT_SET | 6, 0xff);
    bc759x_cmd_send(BLINK_WT_SET | 7, 0xff);
    display_dec(5, Value);  // 在第5，6，7位上显示数值
    display_bar16(0, Bar1); // 显示光柱
    read_xn(0);
    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        // *********** 按键处理 ***********
        if (is_key_changed()) // 如果有新按键变化
        {
            NewKey = get_key_value();
            switch (NewKey)
            {
            case KEY_PLUS: // 如果是“+”键按下
                if (Value < 255)
                {
                    display_dec(5, ++Value);
                }
                break;
            case LP_PLUS:                          // "+"键长按
                bc759x_cmd_send(GLOBAL_CTL, 0x01); // 停止闪烁显示
                while (get_key_value() == LP_PLUS) // 如果是长按键一直持续，Value快速增加
                {
                    if (Value < 255)
                    {
                        display_dec(5, ++Value); // 数值加1,然后刷新数值显示
                    }
                    delay_1ms(100); // 延时100ms
                }
                bc759x_cmd_send(GLOBAL_CTL, 0); // 按键释放，恢复闪烁显示
                break;
            case KEY_MINUS: // “-”键按下
                if (Value > 0)
                {
                    display_dec(5, --Value);
                }
                break;
            case LP_MINUS:                          // "-"键长按
                bc759x_cmd_send(GLOBAL_CTL, 0x01);  // 停止闪烁显示
                while (get_key_value() == LP_MINUS) // 如果是长按键一直持续，Value快速减小
                {
                    if (Value > 0)
                    {
                        display_dec(5, --Value); // 刷新数值显示，然后数值减1
                    }
                    delay_1ms(100); // 延时100ms
                }
                bc759x_cmd_send(GLOBAL_CTL, 0); // 恢复闪烁显示
                break;
            case SHIFT_PLUS: // 如果是SHIFT-PLUG组合键按下 (光柱1增加)
                Bar1 = (Bar1 << 1) | 0x01;
                display_bar16(0, Bar1);
                break;
            case SHIFT_MINUS: // 如果是SHIFT-MINUS组合键按下 (光柱1减小)
                Bar1 = Bar1 >> 1;
                display_bar16(0, Bar1);
                break;
            case 0xFF: // "无按键"的长按键，收到此键值表示距最后一次键盘事件超过了一个长按键时间没有任何键盘操作
                NoKeyTimer++;
                if (NoKeyTimer >= 10) // 如果超过10倍长按键时间
                {
                    NoKeyTimer = 0;
                    bc759x_cmd_send(DIM_CTL, 0x0C); // 显示亮度调为0x0C(进入节能状态)
                }
                break;
            default:
                break;
            }
            if (NewKey != 0xFF) // 对除了"无按键"以外的任何键盘事件
            {
                NoKeyTimer = 0;
                bc759x_cmd_send(DIM_CTL, 0); // 亮度调到最高(从节能状态恢复)
            }
        }
        // ********** 按键处理部分结束 ***********
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        if (++main_loop_cnt > 500000)
        {
            main_loop_cnt = 0;
            HAL_GPIO_TogglePin(RUN_GPIO_Port, RUN_Pin);
        }
        read_di();
        read_xn(5);
        read_pulse();
    }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief TIM4 Initialization Function
  * @param None
  * @retval None
  */
void MX_TIM4_Init(void)
{

  /* USER CODE BEGIN TIM4_Init 0 */

  /* USER CODE END TIM4_Init 0 */

  TIM_Encoder_InitTypeDef sConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM4_Init 1 */

  /* USER CODE END TIM4_Init 1 */
  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 0;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 65535;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
  sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC1Filter = 0;
  sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 0;
  if (HAL_TIM_Encoder_Init(&htim4, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM4_Init 2 */

  /* USER CODE END TIM4_Init 2 */

}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/**
  * @brief USART3 Initialization Function
  * @param None
  * @retval None
  */
void MX_USART3_UART_Init(void)
{

  /* USER CODE BEGIN USART3_Init 0 */

  /* USER CODE END USART3_Init 0 */

  /* USER CODE BEGIN USART3_Init 1 */

  /* USER CODE END USART3_Init 1 */
  huart3.Instance = USART3;
  huart3.Init.BaudRate = 9600;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  huart3.Init.Parity = UART_PARITY_NONE;
  huart3.Init.Mode = UART_MODE_TX_RX;
  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart3.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART3_Init 2 */

  /* USER CODE END USART3_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, OE1_Pin|OE2_Pin|OE3_Pin|OE4_Pin
                          |RUN_Pin, GPIO_PIN_SET);

  /*Configure GPIO pins : KEY6_Pin KEY7_Pin I11_Pin I10_Pin
                           I7_Pin I6_Pin I5_Pin I4_Pin
                           KEY1_Pin KEY2_Pin KEY3_Pin */
  GPIO_InitStruct.Pin = KEY6_Pin|KEY7_Pin|I11_Pin|I10_Pin
                          |I7_Pin|I6_Pin|I5_Pin|I4_Pin
                          |KEY1_Pin|KEY2_Pin|KEY3_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pins : I12_Pin I3_Pin KEY0_Pin */
  GPIO_InitStruct.Pin = I12_Pin|I3_Pin|KEY0_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : I9_Pin I8_Pin KEY5_Pin I2_Pin
                           I1_Pin */
  GPIO_InitStruct.Pin = I9_Pin|I8_Pin|KEY5_Pin|I2_Pin
                          |I1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : OE1_Pin OE2_Pin OE3_Pin OE4_Pin */
  GPIO_InitStruct.Pin = OE1_Pin|OE2_Pin|OE3_Pin|OE4_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pin : KEY4_Pin */
  GPIO_InitStruct.Pin = KEY4_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(KEY4_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : RUN_Pin */
  GPIO_InitStruct.Pin = RUN_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(RUN_GPIO_Port, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
